﻿using System;
using System.Collections.Generic;
using System.Text;
using JetBrains.Annotations;
using UnityEngine;

namespace TPF.ObjectCache
{
    public sealed class PoolCore : MonoBehaviour
    {
        private readonly List<PoolRule> _rules = new List<PoolRule>(); // 存放对象池规则集合
        private PoolRecycleMethod _recycleMethod;

        private readonly Queue<ObjectInstantiateMethod> _objectInstantiateControllerQueue = new Queue<ObjectInstantiateMethod>(); // 说明见MaxAsyncCallbackOneFrame

        internal string PoolTag { get; private set; } // 对象池的标记，用于分类不同的对象池。对象池实例被创建时赋值。
        internal Dictionary<string, Queue<PooledObjectInfo>> PooledObjects { get; private set; } // 池中回收的对象集合
        internal Dictionary<GameObject, ActivatedObjectInfo> ActivatedObjects { get; private set; } // 激活的对象集合，用于回收时快速找到该对象的信息
        internal Dictionary<string, XAsset.Asset> TemplateAssets { get; private set; } // 模板集合
        // 同一帧异步回调的数量限制
        // 对象池的异步加载实际上是在等待AssetBundle的异步加载和重AssetBundle中读取模板资源的异步加载
        // 某些情况下可能业务会注册了很多异步加载的回调，当模板异步加载完成时这些回调都会触发导致当前帧卡顿
        // 所以需要限制一下同一帧最高回调次数，配合_objectInstantiateControllerQueue使用
        internal int MaxAsyncCallbackOneFrame { get; set; }

        [UsedImplicitly]
        private void Awake()
        {
            DontDestroyOnLoad(gameObject);
        }

        [UsedImplicitly]
        private void Update()
        {
            if (_objectInstantiateControllerQueue.Count != 0)
            {
                // 计算当前帧需要实例化多少对象
                var instantiateCount = _objectInstantiateControllerQueue.Count >= MaxAsyncCallbackOneFrame
                    ? MaxAsyncCallbackOneFrame
                    : _objectInstantiateControllerQueue.Count;

                for (var i = 0; i < instantiateCount; i++)
                {
                    var method = _objectInstantiateControllerQueue.Dequeue();

                    if (!method.IsCanceled)
                    {
                        var newObject = (GameObject)Instantiate(method.Asset.asset);
                        OutputObject(newObject, method.Asset.assetPath);
                        method.OnObjectInstantiated(newObject);
                    }
                }
            }
        }

        internal void Initialize(string poolTag)
        {
            PoolTag = poolTag;
            PooledObjects = new Dictionary<string, Queue<PooledObjectInfo>>();
            TemplateAssets = new Dictionary<string, XAsset.Asset>();
            ActivatedObjects = new Dictionary<GameObject, ActivatedObjectInfo>();
        }

        /// <summary>
        /// 将指定规则的集合应用于对象池
        /// </summary>
        /// <param name="rules"></param>
        internal void ApplyRules(string[] rules)
        {
            for (var i = 0; i < rules.Length; i++)
            {
                var ruleType = GetType().Assembly.GetType(rules[i]);
                if (ruleType != null)
                {
                    var ruleInstance = (PoolRule)Activator.CreateInstance(ruleType);
                    ApplyRule(ruleInstance);
                }
            }
        }

        /// <summary>
        /// 将指定规则对象应用于对象池，无法应用相同类型的规则多次
        /// </summary>
        /// <param name="rule"></param>
        internal void ApplyRule(PoolRule rule)
        {
            if (_rules.Find(r => r.GetType() == rule.GetType()) == null)
            {
                _rules.Add(rule);
                rule.OnInitialize(this);
            }
        }

        /// <summary>
        /// 配置回收对象的方法，同一时间只能有一种方法生效
        /// </summary>
        /// <param name="method"></param>
        internal void SetRecycleMethod(PoolRecycleMethod method)
        {
            _recycleMethod = method;
        }

        /// <summary>
        /// 获取一个对象，同步方法
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal GameObject Get(string key)
        {
            var pooledObjects = GetPooledObjects(key);

            GameObject newActiveObject;

            // 如果池中还有对象，直接返回该对象
            if (pooledObjects.Count > 1)
            {
                newActiveObject = pooledObjects.Dequeue().GameObject;
            }
            else
            {
                // 否则加载模板拷贝一份对象
                var templateAsset = GetTemplateAsset(key, false);
                newActiveObject = Instantiate((GameObject)templateAsset.asset);
            }

            return OutputObject(newActiveObject, key);
        }

        /// <summary>
        /// 获取一个对象，异步方法
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        internal PoolRequest GetAsync(string key)
        {
            var pooledObjects = GetPooledObjects(key);

            PoolRequest request;

            // 如果池中还有对象，直接返回该对象
            if (pooledObjects.Count > 1)
            {
                var newActiveObjectInfo = pooledObjects.Dequeue();
                OutputObject(newActiveObjectInfo.GameObject, key);
                request = new PoolRequest(newActiveObjectInfo.GameObject);
            }
            else
            {
                // 否则加载模板拷贝一份对象
                var templateAsset = GetTemplateAsset(key, true);

                if (templateAsset.isDone)
                {
                    // 异步模板资源已加载完成时，从模板资源实例化一份对象传给PoolRequst
                    var newActiveObject = Instantiate((GameObject)templateAsset.asset);
                    OutputObject(newActiveObject, key);
                    request = new PoolRequest(newActiveObject);
                }
                else
                {
                    // 异步模板未加载完成时，将_objectInstantiateControllerQueue注入PoolRequest，统一调度实例化方式
                    request = new PoolRequest(templateAsset, _objectInstantiateControllerQueue);
                }
            }

            return request;
        }

        /// <summary>
        /// 回收对象
        /// 作用对象必须是通过Get/GetAsync获得的对象
        /// </summary>
        /// <param name="obj"></param>
        internal void Recycle(GameObject obj)
        {
            if (obj == null)
            {
                return;
            }
           

            if (!ActivatedObjects.ContainsKey(obj))
            {
                Debug.LogWarning("Error while recycle object, cannot find the object info " + obj);
                Destroy(obj);
                return;
            }

            var info = ActivatedObjects[obj];

            ActivatedObjects.Remove(obj);

            if (_recycleMethod != null)
            {
                _recycleMethod.OnRecycleObject(obj, this);
            }
            else
            {
                Debug.LogError("Recycle method is missing. Pool : " + PoolTag);
            }

            Queue<PooledObjectInfo> pooledObjects;
            if (!PooledObjects.TryGetValue(info.Key, out pooledObjects))
            {
                pooledObjects = new Queue<PooledObjectInfo>();
                PooledObjects.Add(info.Key, pooledObjects);
            }

            pooledObjects.Enqueue(new PooledObjectInfo
            {
                GameObject = obj,
                Key = info.Key,
                RecycleTime = Time.time,
            });

            // 调用注册的规则进行处理
            for (var i = 0; i < _rules.Count; i++)
            {
                _rules[i].OnObjectRecycle(obj, info.Key);
            }
        }

        /// <summary>
        /// 删除指定的对象
        /// </summary>
        /// <param name="key"></param>
        internal void Delete(string key)
        {
            if (key == null)
            {
                return;
            }

            // 
            List<GameObject> toBeRemovedActivatedObjs = null;
            foreach (var kv in ActivatedObjects)
            {
                if (kv.Value.Key == key)
                {
                    if (toBeRemovedActivatedObjs == null)
                    {
                        toBeRemovedActivatedObjs = new List<GameObject>();
                    }

                    toBeRemovedActivatedObjs.Add(kv.Key);
                }
            }

            if (toBeRemovedActivatedObjs != null)
            {
                for (var i = 0; i < toBeRemovedActivatedObjs.Count; i++)
                {
                    ActivatedObjects.Remove(toBeRemovedActivatedObjs[i]);
                    Destroy(toBeRemovedActivatedObjs[i]);
                }
            }

            // 
            if (PooledObjects.ContainsKey(key))
            {
                var pooledObjects = PooledObjects[key];
                while (pooledObjects.Count != 0)
                {
                    Destroy(pooledObjects.Dequeue().GameObject);
                }

                PooledObjects.Remove(key);
            }

            // 
            if (TemplateAssets.ContainsKey(key))
            {
                TemplateAssets[key].Release();
                TemplateAssets.Remove(key);
            }
        }

        /// <summary>
        /// 清除对象池
        /// 包括所有激活的对象，池中对象及模板
        /// </summary>
        internal void Clear()
        {
            // 清理激活的对象
            foreach (var activatedObjet in ActivatedObjects.Keys)
            {
                Destroy(activatedObjet);
            }

            ActivatedObjects.Clear();

            // 清理池中的对象
            foreach (var pooledObjectsQueue in PooledObjects.Values)
            {
                while (pooledObjectsQueue.Count != 0)
                {
                    Destroy(pooledObjectsQueue.Dequeue().GameObject);
                }
            }

            PooledObjects.Clear();

            // 清理模板
            foreach (var templateAsset in TemplateAssets.Values)
            {
                templateAsset.Release();
            }

            TemplateAssets.Clear();

            // 清理_objectInstantiateControllerQueue
            if (_objectInstantiateControllerQueue.Count > 0)
            {
                _objectInstantiateControllerQueue.Clear();
            }

            // 调用注册的规则进行处理
            for (var i = 0; i < _rules.Count; i++)
            {
                _rules[i].OnClear();
            }
        }

        /// <summary>
        /// 打印出当前对象池的状态
        /// </summary>
        internal void Dump()
        {
            var sb = new StringBuilder();
            sb.AppendLine("-----------------------------------------------------");
            sb.AppendLine("Pool Tag: \t\t\t" + PoolTag);
            sb.AppendLine("MaxAsyncCallbackOneFrame: \t" + MaxAsyncCallbackOneFrame);
            sb.AppendLine("PoolRecycleMethod: \t\t" + _recycleMethod.GetType().Name);
            sb.AppendLine("Pool Rule List: \t\t" + (_rules.Count == 0 ? "empty" : ""));
            for (var i = 0; i < _rules.Count; i++)
            {
                sb.AppendLine("\t" + _rules[i].GetType().Name);
            }
            sb.AppendLine("Template Assets Count: \t" + TemplateAssets.Count);
            sb.AppendLine("Activated Objects Count: \t" + ActivatedObjects.Count);
            sb.AppendLine("Pooled Objects:");
            foreach (var pooledObject in PooledObjects)
            {
                sb.AppendLine("\t" + pooledObject.Key + ", count: " + pooledObject.Value.Count);
            }

            sb.AppendLine("Waiting Instantiate Count: \t" + _objectInstantiateControllerQueue.Count);
            sb.AppendLine("-----------------------------------------------------");

            Debug.Log(sb.ToString());
        }

        /// <summary>
        /// 根据资源对象的key值获取池对象
        /// </summary>
        /// <returns></returns>
        private Queue<PooledObjectInfo> GetPooledObjects(string key)
        {
            Queue<PooledObjectInfo> pooledObjects;
            if (!PooledObjects.TryGetValue(key, out pooledObjects))
            {
                pooledObjects = new Queue<PooledObjectInfo>();
                PooledObjects.Add(key, pooledObjects);
            }

            return pooledObjects;
        }


        /// <summary>
        /// 根据资源对象的key值获取对象模板Asset
        /// </summary>
        /// <param name="key"></param>
        /// <param name="async"></param>
        /// <returns></returns>
        private XAsset.Asset GetTemplateAsset(string key, bool async)
        {
            XAsset.Asset templateAsset;
            if (!TemplateAssets.TryGetValue(key, out templateAsset))
            {
                templateAsset = async ? XAsset.Assets.LoadAsync<GameObject>(key) : XAsset.Assets.Load<GameObject>(key);
                TemplateAssets.Add(key, templateAsset);
            }

            return templateAsset;
        }

        private GameObject OutputObject(GameObject go, string key)
        {
            // 记录输出的对象信息
            ActivatedObjects.Add(go, new ActivatedObjectInfo
            {
                ActivateTime = Time.time,
                Key = key,
                ObjectPoolTag = tag
            });

            if (_recycleMethod != null)
            {
                _recycleMethod.OnGetObject(go, this);
            }
            else
            {
                Debug.LogError("Recycle method is missing. Pool : " + PoolTag);
            }

            // 调用注册的规则进行处理
            for (var i = 0; i < _rules.Count; i++)
            {
                _rules[i].OnObjectActivate(go, key);
            }

            return go;
        }
    }
}